#!/usr/bin/python -S
"""
node.py

Python emulation of node.js event loop for Poly.

He's right: coroutines are a big abstraction over events.  Events are low level.
Closures should be enough??

BAD terminology... epoll emits SYSTEM events.  

event HANDLERS -- handle KERNEL events, queue USER events
event LISTNERS -- or callbacks, handle USER events
  use with .on()

"""

__author__ = 'Andy Chu'


import os
#import popen2
import sys
import select
import subprocess
import time

import base

import ioloop

class Error(Exception):
  pass


# NOTE: Subsumed by add_callback in ioloop.py?
class WakingQueue(object):
  """
  A wrapper around a thread-safe queue that also generates an event when there is
  something to consume.

  We use a self-pipe to wake the event loop.

  collections.deque is implemented in C, might release GIL, so not safe to use
  from multiple threads
    # append, popleft

  I could just make my own array... although it has to have its own size
  you could make it a stack... lifo order -- it might matter if the event loop
  gets too busy?  the earlier ones would start?  not sure

  Does this queue also need to handle "return events"?

  What about  responses?

  q = WakingQueue()
  # EVENT q:put
  def ProcessRequest(yo):
    p
  q.on('put', ProcessRequest)
  """
  def __init__(self, pipe, event_loop=None):
    self.event_loop = event_loop
    self.queue = Queue.Queue()
    self.pipe = pipe  # TODO: create this pipe, and add it to the event loop

  def put(self, item):
    # this one is accessed from multiple threads?
    # I think since it just does "append" 

    self.queue.put(item)
    # write to a pipe now
    # this will cause the event loop to wake up with an event.  might need an
    # object there to be the "other side" of the queue.

  def get(self, item):
    # this is accessed from a single thread -- the event loop
    item = self.queue.get(item)
    # We must have gotten an item here, because we only call get() when we are
    # woken up
    assert item, 'expected item in queue'
    return item


# TODO:
# Queue()
# - Start N threads that write requests randomly into this single queue
# - Every time they write, write a byte to pipe that unblocks the select loop.
# then the select loop will pop from the queue (nonblocking) and assert that it
# got something

#
# Timer
#
# look at eventlet/hub.py add_timer
# keep a data structure of the next ones -- uses heappush
# calls self.wait(), whcih is implemented by subclasses of hub
# which calls time.sleep().  so it is manual.  nice.

#
# DISK FILES - easy

# WRITING TO pipes
# look how node-js avoids blocking
#
# WRITING to socks -- for HTTP client. look httplib async


def main(argv):
  #SelectTest(argv)
  action = argv[1]
  if action =='select':
    SelectTest(argv)
  elif action =='proc':
    ProcTest(argv)
  elif action =='block':
    BlockTest(argv)
  elif action == 'pipe':
    PipeTest(argv)
  else:
    LoopTest(argv)
  return 0


if __name__ == '__main__':
  try:
    sys.exit(main(sys.argv))
  except Error, e:
    print >> sys.stderr, e.args[0]
    sys.exit(1)
